﻿//=============================================================================
// Библиотека 'WPFExcelReport' предназначена для связи и передачи данных c PLC
//=============================================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace WPFExcelReport.CL
{
    /// <summary>
    /// Items S7
    /// </summary>
    public class Items
    {
        #region Constants

        #endregion

        #region Fields

        #region General structure

        /// <summary>
        /// Deskr-s for reading variables: key - the name of the variable
        /// </summary>
        private Dictionary<string, HandlItemRead> _hItemRead = new Dictionary<string, HandlItemRead>(100);

        /// <summary>
        /// Deskr-s variables for the record: key - the name of the variable
        /// </summary>
        private Dictionary<string, HandlItemWrite> _hItemWrite = new Dictionary<string, HandlItemWrite>(100);

        /// <summary>
        /// The values of variables to read: key - the name of the variable
        /// </summary>
        private Dictionary<string, object> _iValueRead = new Dictionary<string, object>(100);

        /// <summary>
        /// Addresses of variables to read: key - the name of the variable
        /// </summary>
        private Dictionary<string, string> _iAddrRead = new Dictionary<string, string>(100);

        /// <summary>
        /// Addresses of variables for the record: key - the name of the variable
        /// </summary>
        private Dictionary<string, string> _iAddrWrite = new Dictionary<string, string>(100);

        /// <summary>
        /// Server S7
        /// </summary>
        private S7.S7 _server = new S7.S7();

        #endregion

        /// <summary>
        /// Num error
        /// </summary>
        private int _errNum = 0;

        /// <summary>
        /// Description of errors
        /// </summary>
        private string _errTxt = "";
              
        #endregion

        #region Properties

        /// <summary>
        /// Num error
        /// </summary>
        public int ErrNum
        {
            get
            {
                return _errNum;
            }
        }

        /// <summary>
        /// Description of error
        /// </summary>
        public string ErrTxt
        {
            get
            {
                return _errTxt;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor Items
        /// </summary>
        internal Items(Dictionary<string, HandlItemRead> hItemRead,
                       Dictionary<string, HandlItemWrite> hItemWrite,
                       Dictionary<string, object> iValueRead,
                       Dictionary<string, string> iAddrRead,
                       Dictionary<string, string> iAddrWrite,
                       S7.S7 server)
        {
            this._hItemRead = hItemRead;
            this._hItemWrite = hItemWrite;
            this._iValueRead = iValueRead;
            this._iAddrRead = iAddrRead;
            this._iAddrWrite = iAddrWrite;
            this._server = server;
        }

        #endregion

        #region Internal methods

        /// <summary>
        /// Parse err
        /// </summary>
        private void ParseErrCode(ErrCode err, string addr = "")
        {
            switch (err)
            {
                case ErrCode.Ok:
                    _errNum = (int)ErrCode.Ok;
                    _errTxt = "Ок";
                    break;

                case ErrCode.XmlNoNode:
                    _errNum = (int)ErrCode.XmlNoNode;
                    _errTxt = "The XML file is not valid - the absence subsites Item";
                    break;

                case ErrCode.SameName:
                    _errNum = (int)ErrCode.SameName;
                    _errTxt = "Variable name " + addr + " already exists";
                    break;

                case ErrCode.SameAddr:
                    _errNum = (int)ErrCode.SameAddr;
                    _errTxt = "Variable addr" + addr + " already exists";
                    break;

                case ErrCode.NameNotFound:
                    _errNum = (int)ErrCode.NameNotFound;
                    _errTxt = "Variable " + addr + " no exists";
                    break;

                case ErrCode.ItemReadNoCorrect:
                    _errNum = (int)ErrCode.ItemReadNoCorrect;
                    _errTxt = "Addr " + addr + " variable for reading not correct";
                    break;

                case ErrCode.ItemWriteNoCorrect:
                    _errNum = (int)ErrCode.ItemWriteNoCorrect;
                    _errTxt = "Addr " + addr + " variable recording is not correct";
                    break;

                case ErrCode.VarTypeOther:
                    _errNum = (int)ErrCode.VarTypeOther;
                    _errTxt = "Type " + addr + " not determined";
                    break;

            }
        }

        /// <summary>
        /// Проверка для добавления переменной для чтения
        /// </summary>
        ///<param name="addr"> адрес переменной</param>
        ///<param name="name"> имя переменной</param>
        ///<param name="itemsRead"> элементы для сервера</param>
        ///<param name="iAddrRead"> адреса переменных </param>
        private ErrCode CheckIRead(string addr, string name, List<ItemRead> itemsRead, Dictionary<string, string> iAddrRead)
        {
            // проверка на совпадение имени
            if (iAddrRead.ContainsKey(name) || _iAddrRead.ContainsKey(name))
            {
                ParseErrCode(ErrCode.SameName, name);
                return ErrCode.SameName;
            }

            // проверка на совпадение адреса
            if (iAddrRead.ContainsValue(addr) || _iAddrRead.ContainsValue(addr))
            {
                ParseErrCode(ErrCode.SameAddr, addr);
                return ErrCode.SameAddr;
            }

            // добавляем переменные
            iAddrRead.Add(name, addr);
            itemsRead.Add(new ItemRead(name, addr));

            return ErrCode.Ok;
        }

        /// <summary>
        /// Проверка для добавления переменной для записи
        /// </summary>
        ///<param name="addr"> адрес переменной</param>
        ///<param name="name"> имя переменной</param>
        ///<param name="itemsWrite"> элементы для сервера</param>
        ///<param name="iAddrWrite"> адреса переменных </param>
        private ErrCode CheckIWrite(string addr, string name, List<ItemWrite> itemsWrite, Dictionary<string, string> iAddrWrite)
        {
            // проверка на совпадение имени
            if (iAddrWrite.ContainsKey(name) || _iAddrWrite.ContainsKey(name))
            {
                ParseErrCode(ErrCode.SameName, name);
                return ErrCode.SameName;
            }

            // проверка на совпадение адреса
            if (iAddrWrite.ContainsValue(addr) || _iAddrWrite.ContainsValue(addr))
            {
                ParseErrCode(ErrCode.SameAddr, addr);
                return ErrCode.SameAddr;
            }

            // добавляем переменные
            iAddrWrite.Add(name, addr);
            itemsWrite.Add(new ItemWrite(name, addr));

            return ErrCode.Ok;
        }

        /// <summary>
        /// Проверка адреса переменной
        /// </summary>
        ///<param name="addr"> адрес</param>
        ///<param name="typeRW"> тип переменной</param>
        ///<returns> ошибка</returns>
        private ErrCode CheckAddr(string addr, TypeRW typeRW)
        {
            if (!_server.IsAddrValid(addr))
            {
                if (typeRW == TypeRW.read)
                {
                    ParseErrCode(ErrCode.ItemReadNoCorrect, addr);
                    return ErrCode.ItemReadNoCorrect;
                }
                else
                {
                    ParseErrCode(ErrCode.ItemWriteNoCorrect, addr);
                    return ErrCode.ItemWriteNoCorrect;
                }
            }

            return ErrCode.Ok;

        }

        #endregion

        #region External methods

        /// <summary>
        /// Adding a variable
        /// </summary>
        /// <param name="addr"> addr var </param>
        /// <param name="name"> name var </param>
        /// <param name="itemType"> type var</param>
        /// <param name="err"> err </param>
        public void Add(string addr, string name, TypeRW itemType, out bool err)
        {
            List<ItemRead> itemsRead = new List<ItemRead>(1);
            List<ItemWrite> itemsWrite = new List<ItemWrite>(1);
            Dictionary<string, string> iAddrRead = new Dictionary<string, string>(1);
            Dictionary<string, string> iAddrWrite = new Dictionary<string, string>(1);
                      
            switch (itemType)
            {
                case TypeRW.read:
                    if (CheckIRead(addr, name, itemsRead, iAddrRead) != ErrCode.Ok) { err = true; return; }

                    if (CheckAddr(addr, TypeRW.read) != ErrCode.Ok) { err = true; return; }
                    break;

                case TypeRW.write:
                    if (CheckIWrite(addr, name, itemsWrite, iAddrWrite) != ErrCode.Ok) { err = true; return; }

                    if (CheckAddr(addr, TypeRW.write) != ErrCode.Ok) { err = true; return; }
                    break;

                default:
                    break;
            }
            
            // добавляем переменные
            foreach (KeyValuePair<string, string> pair in iAddrRead)
            {
                _iAddrRead.Add(pair.Key, pair.Value);
                _iValueRead.Add(pair.Key, new object());
            }

            List<HandlItemRead> hRead = _server.AddItemsRead(itemsRead, out err);
            foreach (HandlItemRead handl in hRead) _hItemRead.Add(handl.Name, handl);

            foreach (KeyValuePair<string, string> pair in iAddrWrite) _iAddrWrite.Add(pair.Key, pair.Value);

            List<HandlItemWrite> hWrite = _server.AddItemsWrite(itemsWrite, out err);
            foreach (HandlItemWrite handl in hWrite) _hItemWrite.Add(handl.Name, handl);

            err = false;
        }

        /// <summary>
        /// Adding a variable from list
        /// </summary>
        /// <param name="vars"> var collect </param>
        /// <param name="err"> err</param>
        public void Add(List<VarData> vars, out bool err)
        {
            List<ItemRead> itemsRead = new List<ItemRead>(vars.Count);
            List<ItemWrite> itemsWrite = new List<ItemWrite>(vars.Count);
            Dictionary<string, string> iAddrRead = new Dictionary<string, string>(vars.Count);
            Dictionary<string, string> iAddrWrite = new Dictionary<string, string>(vars.Count);

            foreach (VarData var in vars)
            {
                switch (var.TypeRW)
                {
                    case TypeRW.read:
                        if (CheckIRead(var.Addr, var.Name, itemsRead, iAddrRead) != ErrCode.Ok) { err = true; return; }

                        if (CheckAddr(var.Addr, TypeRW.read) != ErrCode.Ok) { err = true; return; }
                        break;

                    case TypeRW.write:
                        if (CheckIWrite(var.Addr, var.Name, itemsWrite, iAddrWrite) != ErrCode.Ok) { err = true; return; }

                        if (CheckAddr(var.Addr, TypeRW.write) != ErrCode.Ok) { err = true; return; }
                        break;
                }
            }

            // добавляем переменные
            foreach (KeyValuePair<string, string> pair in iAddrRead)
            {
                _iAddrRead.Add(pair.Key, pair.Value);
                _iValueRead.Add(pair.Key, new object());
            }
          
            List<HandlItemRead> hRead = _server.AddItemsRead(itemsRead, out err);
            foreach (HandlItemRead handl in hRead) _hItemRead.Add(handl.Name, handl);

            foreach (KeyValuePair<string, string> pair in iAddrWrite) _iAddrWrite.Add(pair.Key, pair.Value);

            List<HandlItemWrite> hWrite = _server.AddItemsWrite(itemsWrite, out err);
            foreach (HandlItemWrite handl in hWrite) _hItemWrite.Add(handl.Name, handl);
           
            err = false;
        }

        /// <summary>
        /// Adding a variable from xml
        /// </summary>
        /// <param name="xDoc"> xml doc</param>
        /// <param name="err"> err </param>
        public void Add(XDocument xDoc, out bool err)
        {
            IEnumerable<XElement> items = xDoc.Descendants("Item");

            List<ItemRead> itemsRead = new List<ItemRead>(items.Count());
            List<ItemWrite> itemsWrite = new List<ItemWrite>(items.Count());
            Dictionary<string, string> iAddrRead = new Dictionary<string, string>(items.Count());
            Dictionary<string, string> iAddrWrite = new Dictionary<string, string>(items.Count());

            foreach (XElement item in items)
            {
                string name = item.Element("Name").Value;
                string addr = item.Element("Addr").Value;
                string typeRW = item.Element("TypeRW").Value.ToLower();
                                
                if ((addr != "") && (name != ""))
                {
                    if (typeRW == "read")
                    {
                        if (CheckIRead(addr, name, itemsRead, iAddrRead) != ErrCode.Ok) { err = true; return; }

                        if (CheckAddr(addr, TypeRW.read) != ErrCode.Ok) { err = true; return; }
                        continue;
                    }

                    if (typeRW == "write")
                    {
                        if (CheckIWrite(addr, name, itemsWrite, iAddrWrite) != ErrCode.Ok) { err = true; return; }

                        if (CheckAddr(addr, TypeRW.write) != ErrCode.Ok) { err = true; return; }
                        continue;
                    }

                    ParseErrCode(ErrCode.VarTypeOther, name);
                    err = true; return;
                }
            }

            // добавляем переменные
            foreach (KeyValuePair<string, string> pair in iAddrRead)
            {
                _iAddrRead.Add(pair.Key, pair.Value);
                _iValueRead.Add(pair.Key, new object());
            }

            List<HandlItemRead> hRead = _server.AddItemsRead(itemsRead, out err);
            foreach (HandlItemRead handl in hRead) _hItemRead.Add(handl.Name, handl);

            foreach (KeyValuePair<string, string> pair in iAddrWrite) _iAddrWrite.Add(pair.Key, pair.Value);

            List<HandlItemWrite> hWrite = _server.AddItemsWrite(itemsWrite, out err);
            foreach (HandlItemWrite handl in hWrite) _hItemWrite.Add(handl.Name, handl);

            err = false;
        }

        /// <summary>
        /// Deleting a variable named
        /// </summary>
        /// <param name="name"> name var </param>
        /// <param name="itemType"> type var: 1-read, 2-write</param>
        /// <param name="err"> err </param>
        public void Remove(string name, TypeRW itemType, out bool err)
        {
            switch (itemType)
            {
                case TypeRW.read:
                    if (!_iAddrRead.ContainsKey(name))
                    {
                        ParseErrCode(ErrCode.NameNotFound, name);
                        err = true; return;
                    }
                     _server.DelItemsRead(new List<HandlItemRead>() { _hItemRead[name] }, out err);
                    _hItemRead.Remove(name);

                    _iAddrRead.Remove(name);
                    _iValueRead.Remove(name);
                    break;

                case TypeRW.write:
                    if (!_iAddrWrite.ContainsKey(name))
                    {
                        ParseErrCode(ErrCode.NameNotFound, name);
                        err = true; return;
                    }
                    _server.DelItemsWrite(new List<HandlItemWrite>() { _hItemWrite[name] }, out err);
                    _hItemWrite.Remove(name);

                    _iAddrWrite.Remove(name);
                    break;

                default:
                    break;
            }
                        
            err = false;

        }
               
        /// <summary>
        /// Deleting a var from xml
        /// </summary>
        /// <param name="xDoc"> xml doc</param>
        /// <param name="err"> err </param>
        public void Remove(XDocument xDoc, out bool err)
        {
            IEnumerable<XElement> items = xDoc.Descendants("Item");

            List<string> itemsRead = new List<string>(items.Count());
            List<HandlItemRead> hRead = new List<HandlItemRead>(items.Count());

            List<string> itemsWrite = new List<string>(items.Count());
            List<HandlItemWrite> hWrite = new List<HandlItemWrite>(items.Count());
                   
            foreach (XElement item in items)
            {
                string name = item.Element("Name").Value;
                string typeRW = item.Element("TypeRW").Value.ToLower();

                if (name != "")
                {
                    if (typeRW == "read")
                    {
                        if (!_iAddrRead.ContainsKey(name))
                        {
                            ParseErrCode(ErrCode.NameNotFound, name);
                            err = true; return;
                        }
                        itemsRead.Add(name);
                        hRead.Add(_hItemRead[name]);
                        continue;
                    }

                    if (typeRW == "write")
                    {
                        if (!_iAddrWrite.ContainsKey(name))
                        {
                            ParseErrCode(ErrCode.NameNotFound, name);
                            err = true; return;
                        }
                        itemsWrite.Add(name);
                        hWrite.Add(_hItemWrite[name]);
                        continue;
                    }

                    ParseErrCode(ErrCode.VarTypeOther, name);
                    err = true; return;
                }
            }

            // удаляем переменные
            _server.DelItemsRead(hRead, out err);
            _server.DelItemsWrite(hWrite, out err);

            foreach (string n in itemsRead)
            {
                _iAddrRead.Remove(n);
                _iValueRead.Remove(n);
                _hItemRead.Remove(n);
            }

            foreach (string n in itemsWrite)
            {
                _iAddrWrite.Remove(n);
                _hItemWrite.Remove(n);
            }
                                  
            err = false;

        }

        /// <summary>
        /// Deleting a var from list
        /// </summary>
        /// <param name="vars"> var collect </param>
        /// <param name="err"> err</param>
        public void Remove(List<VarData> vars, out bool err)
        {
            List<string> itemsRead = new List<string>(vars.Count());
            List<HandlItemRead> hRead = new List<HandlItemRead>(vars.Count());

            List<string> itemsWrite = new List<string>(vars.Count());
            List<HandlItemWrite> hWrite = new List<HandlItemWrite>(vars.Count());
        
            foreach (VarData var in vars)
            {
                switch (var.TypeRW)
                {
                    case TypeRW.read:
                        if (!_iAddrRead.ContainsKey(var.Name))
                        {
                            ParseErrCode(ErrCode.NameNotFound, var.Name);
                            err = true; return;
                        }
                        itemsRead.Add(var.Name);
                        hRead.Add(_hItemRead[var.Name]);
                        break;

                    case TypeRW.write:
                         if (!_iAddrWrite.ContainsKey(var.Name))
                        {
                            ParseErrCode(ErrCode.NameNotFound, var.Name);
                            err = true; return;
                        }
                        itemsWrite.Add(var.Name);
                        hWrite.Add(_hItemWrite[var.Name]);
                        break;
                }
            }

            // удаляем переменные
            _server.DelItemsRead(hRead, out err);
            _server.DelItemsWrite(hWrite, out err);

            foreach (string n in itemsRead)
            {
                _iAddrRead.Remove(n);
                _iValueRead.Remove(n);
                _hItemRead.Remove(n);
            }

            foreach (string n in itemsWrite)
            {
                _iAddrWrite.Remove(n);
                _hItemWrite.Remove(n);
            }

            err = false;
        }

        /// <summary>
        /// Returns the element at the specified name
        /// or null, if there is no
        /// </summary>
        ///<param name="name"> name var</param>
        ///<returns> object var</returns>
        public VarData ElementByName(string name)
        {
            VarData res = null;

            if (_iAddrRead.ContainsKey(name))
            {
                res = new VarData();
                res.Addr = _iAddrRead[name];
                res.Name = name;
                res.TypeRW = TypeRW.read;
                res.Value = _iValueRead[name];
            }
            else if (_iAddrWrite.ContainsKey(name))
            {
                res = new VarData();
                res.Addr = _iAddrWrite[name];
                res.Name = name;
                res.TypeRW = TypeRW.write;
                res.Value = new object();
            }

            return res;
        }        
        
        /// <summary>
        /// Returns vars as xml
        /// </summary>
        public XDocument GetDataAsXml()
        {
            XDocument xDoc = new XDocument(new XElement("Items"));

            foreach (KeyValuePair<string, string> val in _iAddrRead)
            {
                xDoc.Element("Items").Add(new XElement("Item",
                                          new XElement("Name", val.Key),
                                          new XElement("Addr", val.Value),
                                          new XElement("Value", _iValueRead[val.Key]),
                                          new XElement("TypeRW", "read")));
            }

            foreach (KeyValuePair<string, string> val in _iAddrWrite)
            {
                xDoc.Element("Items").Add(new XElement("Item",
                                          new XElement("Name", val.Key),
                                          new XElement("Addr", val.Value),
                                          new XElement("TypeRW", "write")));
            }

            return xDoc;
        }

        /// <summary>
        /// Returns vars as list
        /// </summary>
        public List<VarData> GetData()
        {
            List<VarData> res = new List<VarData>(_iAddrRead.Count + _iAddrWrite.Count);

            foreach (KeyValuePair<string, string> item in _iAddrRead)
            {
                VarData val = new VarData();
                val.Addr = item.Value;
                val.Name = item.Key;
                val.Value = _iValueRead[item.Key];
                val.TypeRW = TypeRW.read;

                res.Add(val);
            }

            foreach (KeyValuePair<string, string> item in _iAddrWrite)
            {
                VarData val = new VarData();
                val.Addr = item.Value;
                val.Name = item.Key;
                val.Value = new object();
                val.TypeRW = TypeRW.write;

                res.Add(val);
            }

            return res;
        }

        /// <summary>
        /// Deleting all variables
        /// </summary>
        public void Clear()
        {
            _server.ClearItemsRead();
            _server.ClearItemsWrite();

            _hItemRead.Clear();
            _hItemWrite.Clear();

            _iAddrRead.Clear();
            _iValueRead.Clear();
            _iAddrWrite.Clear();
                      
        }


        #endregion
            
    }
}
